import random
import re
from datetime import datetime
from flask import current_app, jsonify
from flask import make_response
from flask import request
from flask import session

from info import redis_store, db  #从info下导入数据库操作对象redis_store
from info.modules import passport_blu
from info.utils.captcha.captcha import captcha
from info.constants import IMAGE_CODE_REDIS_EXPIRES # 引入常量文件中的常量
from info.response_code import RET  # 导入提示状态码包 并引用
from info.utils.yuntongxun.sms import CCP #导入发送短信的类
from info import constants
from info.models import User

# 因为不涉及任何数据的提交到后端所以也用get请求
@passport_blu.route("/logout",methods=['POST'])
def logout():
    """用户退出登陆"""
    # 清除当前用户的登陆信息  session.pop()删除掉指定成员
    session.pop("user_id",None)
    session.pop("mobile",None)
    session.pop("nick_name",None)
    # 清楚过后直接刷新页面
    return jsonify(errno=RET.OK,errmsg="OK")

@passport_blu.route("/login",methods=['POST'])
def login():
    """用户登陆"""
    # 1.获取参数和判断是否有值
    param_dict = request.json
    if not param_dict:
        # 参数不全
        return jsonify(errno=RET.PARAMERR, errmsg="参数错误")
    mobile = param_dict.get("mobile")
    print(mobile)
    password = param_dict.get("password")

    # 2.从数据库查询出指定的用户
    try:
        #查询数据库中指定用户字段的电话号是否与接收到的相同
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询数据错误")
    # 如果不存在此用户就返回错误信息
    print(user)
    if not user:
        return jsonify(errno=RET.USERERR, errmsg="用户不存在")
    # 3.校验密码: 调用models表模型里面的check_password函数方法
    #  将用户提交过来的password进行校验
    if not user.check_password(password):
        # 返回的是布尔值 True或 False
        return jsonify(errno=RET.PWDERR, errmsg="密码错误")
    # 4.保存用户登录状态
    session["user_id"] = user.id
    session["nick_name"] = user.nick_name
    session["mobile"] = user.mobile
    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)

    # 修改用户的最后登陆时间保存到数据库中
    # 对整体功能不影响所以出错只存到日志中不报错，不必阻止用户登陆
    user.last_login = datetime.now()
    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)

    # 5.返回结果
    return jsonify(errno=RET.OK,errmsg="OK")

@passport_blu.route("/register", methods=['POST']) # 指定只能通过post来发送请求
def register():
    """用户注册"""
    # 1.获取参数和判断是否有值 图片验证码已经在发送短信的时候校验过了
    param_dict = request.json
    mobile = param_dict.get("mobile")
    password = param_dict.get("password")
    sms_code = param_dict.get("sms_code")
    # 验证用户提交上来的数据
    if not all([mobile,password,sms_code]):
        # 参数不全,以json的格式返回错误给前段ajax的请求
        return jsonify(errno = RET.PARAMERR,errmsg = "参数不全")
    # 2.校验手机号是否正确
    if not re.match("^1[356789][0-9]{9}$", mobile):
        # 提示手机号不正确
        return jsonify(errno=RET.DATAERR, errmsg="手机号不正确")
    # 2.1 校验手机是否被使用过

    # 3.从redis中获取指定手机号对应的短信验证码的
    try:
        real_sms_code = redis_store.get("SMS_%s" % mobile)
        # 保存在redis的数据，最终都是以字节码的格式保存，所以转换编码
        real_sms_code = real_sms_code.decode()
        if not real_sms_code:
            # 短信验证码已经过期
            return jsonify(errno=RET.DATAERR, errmsg="短信验证码已过期")
        # 如果能够取出来值，删除redis中缓存的内容短信验证码
        # 保证每次取出的短信验证码内容都只能使用一次
        redis_store.delete("sms_code_" + mobile)

    except Exception as e:
        current_app.logger.error(e)
        # 获取短信验证码失败
        return jsonify(errno=RET.DBERR, errmsg="获取短信验证码失败")
    # 把用户提交过来的短信验证码与redis中的真实验证码进行判定
    if real_sms_code!= sms_code:
        return jsonify(errno=RET.DATAERR, errmsg="短信验证码错误")
    # 根据手机号到数据库中查询，当前手机号是否已经被注册
    num = User.query.filter(User.mobile==mobile).count()
    if num > 0:
        return jsonify(errno=RET.DATAERR, errmsg="手机号已注册")
    # 4.保存用户信息 数据表有这个键(mobile,password)的值就修改 没有则添加进去
    user = User() # 调用模型表的User类拿到用户模型
    # 数据表有这个键(mobile, password)的值就修改没有则添加进去 键值对的形式保存
    user.mobile = mobile
    #直接调用password它会帮我们调用表模型类里面的 @password.setter,
    #它里面的函数的值password_hash会被函数generate_password_hash(value)处理,自动
    # 把我们的密码进行加密，而它里面的value值就是我们传进去的password密码
    user.password = password
    user.nick_name = mobile #使用手机号作为用户的名称显示在页面登陆边角
    # 4. 设置数据并添加到数据库
    try:
        # 把user user的id是唯一1，这一表的字段添加到数据表中
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        db.session.rollback() # 回滚事务
        current_app.looger.error(e)
        #数据保存错误
        return jsonify(errno=RET.DATAERR,errmsg="数据保存错误")
    # 4.1 注册成功后自动保存当前用户的登陆状态
    #这里的session保存的是用户登陆存在浏览器里面的session信息，是以键值对的方式存在的
    session["user_id"] = user.id
    session["nick_name"] = user.nick_name
    session["mobile"] = mobile
    # 6.返回注册的结果
    return jsonify(errno=RET.OK,errmsg="OK")


@passport_blu.route("/image_code")
def image_code():
    # 图片验证码
    # 接收客户端发送过来的uuid
    code_id = request.args.get("code_id")
    print(code_id) # 拿到的值8f65e99f-fe6f-4dd5-aea7-88ae1ca017e1这种格式就是编码


    """生成图片验证码"""
    # capcha.py文件里面return返回的是一个元组（名称，图片内容，图片)
    name,text,image = captcha.generate_captcha()
    # 这里的text是一个图片验证码里面的内容文本，保存下来
    print(text)

    # 把图片生成验证码的文本内容和uuid拼接组合保存到redis中,
    # 使用string字符串类型类保存数据并设置编号code_id的～
    # 有效期IMAGE_CODE_REDIS_EXPIRES，用常量来设置(去常量文件中取)
    try:
        # text是一个图片验证码里面的内容文本，要保存下来到数据库中
        redis_store.setex("image_code_%s" % code_id,IMAGE_CODE_REDIS_EXPIRES,text)
    except Exception as e:
    #如果出现错误就记录到日志当中
        current_app.logger.error(e)
    # 返回错误信息内容给前端页面 参数[提示状态码 ，错误提示信息]
    #  前端是默认只识别json格式的
        return make_response(jsonify(error=RET.DATAERR,errmsg="保存图片验证码失败"))
    # make_response函数用来构造一个Response对象，第一个参数为响应的正文内容。
    response = make_response(image)
    response.headers["Content-Type"]= "image/jpeg"
    # 直接返回图片内容就可以了
    # 但是这样直接返回会以text/html文本类型返回到页面出现乱码，所以就
    # 需要设置上面的返回格式是图片类型 image/jpeg
    return response

@passport_blu.route("/sms_code",methods = ['POST','GET'])
def sms_code():
    """发送短信"""
    # 1.接收参数并判断是否有值[手机号-图片验证码内容-uuid]
    param_dict = request.json
    mobile = param_dict.get("mobile")
    image_code = param_dict.get("image_code")
    image_code_id = param_dict.get("image_code_id")
    # 校验拿到的所有数据值是否正确
    if not all([mobile,image_code,image_code_id]):
        # 参数不全,以json的格式返回错误给前段ajax的请求
        return jsonify(errno = RET.PARAMERR,errmsg = "参数不全")
    # 2.校验手机号是否正确
    if not re.match("^1[356789][0-9]{9}$", mobile):
        # 提示手机号不正确
        return jsonify(errno=RET.DATAERR, errmsg="手机号不正确")
    # 3.通过传入的图片验证码去redis中查询真实的图片验证码内容进行比对
    try:            # image_code_%s  %s是拼接uuid编号一起传过来的所以这样写
        real_image_code = redis_store.get("image_code_%s" % image_code_id)
        # 保存在redis的数据，最终都是以字节码的格式保存，所以转换编码
        real_image_code = real_image_code.decode()
        if not real_image_code:
            # 验证码已经过期
            return jsonify(errno=RET.PARAMERR, errmsg="图片验证码已过期")
        # 如果能够取出来值，删除redis中缓存的内容验证码与编号
        # 保证每次取出的验证码内容都只能使用一次
        redis_store.delete("image_code_" + image_code_id)

    except Exception as e:
        current_app.logger.error(e)
        # 获取图片验证码失败
        return jsonify(errno=RET.DBERR, errmsg="获取图片验证码失败")
    # 4.进行验证码内容的比对 把填写的验证码字符串转成不区分大小写
    if image_code.lower() != real_image_code.lower():
        # 验证码输入错误
        return jsonify(errno=RET.DATAERR, errmsg="验证码输入错误")
    # 随机生成一个短信验证码 0到9999的范围
    random_num = random.randint(0,9999)
    # 保证验证码即便是两位数三位的也只能是六位验证码
    random_num = "%06d" % random_num

    # 5.生成发送短信的内容并发送短信
    # ccp = CCP()
    # # 注意： 测试的短信模板编号为1 参数一：测试接收的号码 【参数二：短信验证码，参数三：过期时间】；
    # ret = ccp.send_template_sms(mobile, [random_num, constants.SMS_CODE_REDIS_EXPIRES], constants.SMS_TEMPLATE_ID)
    # # print 接收一下ret的值  为-1发送失败 为0发送成功
    # # print(ret)
    # if ret != 0:
    #     # 发送短信失败
    #     return jsonify(errno=RET.THIRDERR, errmsg="发送短信失败")

    # 6.redis中保存短信验证码内容
    try:           #  sms_code验证码   SMS_CODE_REDIS_EXPIRES  redis中保存验证码的期限
        redis_store.setex("SMS_" + mobile, constants.SMS_CODE_REDIS_EXPIRES, random_num)
    except Exception as e:
        current_app.logger.error(e)
        # 保存短信验证码失败
        return jsonify(errno=RET.DBERR, errmsg="保存短信验证码失败")

    # 7.返回发送成功的响应
    return jsonify(errno=RET.OK, errmsg="发送成功")































